package com.pingan.haofang.searchcloud.api.query.parser.element;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;

import com.pingan.haofang.searchcloud.api.query.parser.dbobject.ConditionGroup;
import com.pingan.haofang.searchcloud.api.query.parser.dbobject.SqlNode;
import com.pingan.haofang.searchcloud.api.query.parser.dbobject.sqlnode.ColumnNode;
import com.pingan.haofang.searchcloud.api.query.parser.dbobject.sqlnode.TextNode;
import com.pingan.haofang.searchcloud.api.query.parser.dbobject.sqlnode.aggregate.AggregateFunction;
import com.pingan.haofang.searchcloud.api.query.parser.dbobject.sqlnode.function.Function;
import com.pingan.haofang.searchcloud.api.query.parser.dbobject.util.SqlNodeUtil;
import com.pingan.haofang.searchcloud.api.query.parser.exception.SqlParseException;

/**
 * ClassName: QueryBlock <br>
 * Function: 查询块（标示一次subSelect，如果查询包含子查询，则会被拆解为多个queryBlock表示）
 *
 * @author LUYI374
 * @version 1.0.0
 * @since 1.0.0
 * @date 2015.6.20
 */
public class QueryBlock {

    /**
     * 查询块别名
     */
    private String alias;

    /**
     * 分组字段
     */
    private List<? extends SqlNode> groupByClause;

    /**
     * 排序后的分组字段
     */
    private TreeMap<Integer, SqlNode> sortedGroupByClause;

    /**
     * 排序字段
     */
    private List<SqlNode> orderByClause;

    /**
     * 抓取字段
     */
    private List<SqlNode> selectClause;

    /**
     * 抓取字段集合
     */
    private Map<String, ColumnNode> selectColumnMap;

    /**
     * 抓取的SQL节点集合
     */
    private Map<String, SqlNode> selectNodeMap;

    /**
     * 需要查询的内容名称集合
     */
    private LinkedHashSet<String> selectItemNames;

    /**
     * 分组字段列表
     */
    private Map<Integer, AggregateFunction> aggregateClause;

    /**
     * 字段和分组的关联关系
     */
    private Map<String, Map<Integer, AggregateFunction>> columnAggregateMap;

    /**
     * 函数列表
     */
    private Map<Integer, Function> functionClause;

    /**
     * 分页offset
     */
    private TextNode offset;

    /**
     * 分页记录数
     */
    private TextNode limit;

    /**
     * 表名称集合
     */
    private String tableName;

    /**
     * 表别名
     */
    private String tableAlias;

    /**
     * where条件集合
     */
    private List<ConditionGroup> whereConditionGroupList;

    /**
     * 是否select *（仅限select）
     */
    private boolean wildcard;

    /**
     * OR条件位置索引
     */
    private Set<Integer> orIndexSet;

    /**
     * 是否distinct
     */
    private boolean useDistinct;

    /**
     * 排序字典集合
     */
    private Map<String, ColumnNode> orderByColumnMap;

    /**
     * 排序节点集合
     */
    private Map<String, SqlNode> orderByNodeMap;

    /**
     * 是否存在独占的group by
     */
    private boolean exclusiveGroupBy;

    public QueryBlock() {
        init();
    }

    private void init() {
        selectClause = new ArrayList<SqlNode>();
        orderByClause = new ArrayList<SqlNode>(3);
        orderByColumnMap = new LinkedHashMap<String, ColumnNode>(3);
        selectColumnMap = new LinkedHashMap<String, ColumnNode>();
        selectItemNames = new LinkedHashSet<String>();
        selectNodeMap = new LinkedHashMap<String, SqlNode>();
        orderByNodeMap = new LinkedHashMap<String, SqlNode>();
    }

    /**
     * 添加组函数
     */
    public void addAggregateClause(int index, AggregateFunction node) {
        if (aggregateClause == null) {
            aggregateClause = new HashMap<Integer, AggregateFunction>();
        }

        if (columnAggregateMap == null) {
            columnAggregateMap = new HashMap<String, Map<Integer, AggregateFunction>>(3);
        }
        aggregateClause.put(index, node);

        Map<Integer, AggregateFunction> map = null;
        if (!columnAggregateMap.containsKey(node.getColumn().getColumnName())) {
            map = new HashMap<Integer, AggregateFunction>(3);
            columnAggregateMap.put(node.getColumn().getColumnName(), map);
        } else {
            map = columnAggregateMap.get(node.getColumn().getColumnName());
        }
        map.put(index, node);
    }

    /**
     * 添加普通函数
     * 
     * @param index
     * @param node
     */
    public void addFunctionClause(int index, Function node) {
        if (functionClause == null) {
            functionClause = new HashMap<Integer, Function>();
        }
        functionClause.put(index, node);
    }

    /**
     * 添加select抓取节点
     * 
     * @param fetchNode
     */
    public void addSelectItem(SqlNode node) {
        selectClause.add(node);
        if (selectItemNames.contains(node.getAlias())) {
            throw new SqlParseException("duplicate select node, please check");
        }
        selectItemNames.add(node.getAlias());
        selectNodeMap.put(node.getAlias(), node);
    }

    /**
     * 是否为分组查询
     * 
     * @return
     */
    public boolean useAggregate() {
        return CollectionUtils.isNotEmpty(groupByClause) || MapUtils.isNotEmpty(aggregateClause);
    }

    /**
     * 是否使用了分页
     * 
     * @return
     */
    public boolean usePage() {
        return limit != null;
    }

    /**
     * 添加order by节点
     * 
     * @param node
     * @param index
     * @param asc
     */
    public void addOrderByNode(SqlNode node) {
        orderByClause.add(node);
        orderByNodeMap.put(node.getAlias(), node);
        if (SqlNodeUtil.isColumn(node)) {
            ColumnNode columnNode = (ColumnNode) node;
            orderByColumnMap.put(columnNode.getAlias(), columnNode);
        }
    }

    public void addOrIndexSet(Set<Integer> set) {
        if (CollectionUtils.isEmpty(set)) {
            return;
        }
        if (orIndexSet == null) {
            orIndexSet = new HashSet<Integer>();
        }
        this.orIndexSet.addAll(set);
    }

    public List<? extends SqlNode> getGroupByClause() {
        return groupByClause;
    }

    public void setGroupByClause(List<? extends SqlNode> groupByClause) {
        this.groupByClause = groupByClause;
    }

    public List<SqlNode> getSelectClause() {
        return selectClause;
    }

    public Map<Integer, AggregateFunction> getAggregateClause() {
        return aggregateClause;
    }

    public Map<Integer, Function> getFunctionClause() {
        return functionClause;
    }

    public TextNode getOffset() {
        return offset;
    }

    public void setOffset(TextNode offset) {
        this.offset = offset;
    }

    public TextNode getLimit() {
        return limit;
    }

    public void setLimit(TextNode limit) {
        this.limit = limit;
    }

    public List<SqlNode> getOrderByClause() {
        return orderByClause;
    }

    public List<ConditionGroup> getWhereConditionGroupList() {
        return whereConditionGroupList;
    }

    public void setWhereConditionGroupList(List<ConditionGroup> whereConditionGroupList) {
        this.whereConditionGroupList = whereConditionGroupList;
    }

    public String getAlias() {
        return alias;
    }

    public boolean isWildcard() {
        return wildcard;
    }

    public void setWildcard(boolean wildcard) {
        this.wildcard = wildcard;
    }

    public String getTableName() {
        return tableName;
    }

    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    public String getTableAlias() {
        return tableAlias;
    }

    public void setTableAlias(String tableAlias) {
        this.tableAlias = tableAlias;
    }

    public Set<Integer> getOrIndexSet() {
        return orIndexSet;
    }

    public void setOrIndexSet(Set<Integer> orIndexSet) {
        this.orIndexSet = orIndexSet;
    }

    public Map<String, ColumnNode> getSelectColumnMap() {
        return selectColumnMap;
    }

    public Map<String, Map<Integer, AggregateFunction>> getColumnAggregateMap() {
        return columnAggregateMap;
    }

    public LinkedHashSet<String> getSelectItemNames() {
        return selectItemNames;
    }

    public boolean useDistinct() {
        return useDistinct;
    }

    public void setUseDistinct(boolean useDistinct) {
        this.useDistinct = useDistinct;
    }

    public Map<String, ColumnNode> getOrderByColumnMap() {
        return orderByColumnMap;
    }

    public TreeMap<Integer, SqlNode> getSortedGroupByClause() {
        return sortedGroupByClause;
    }

    public void setSortedGroupByClause(TreeMap<Integer, SqlNode> sortedGroupByClause) {
        this.sortedGroupByClause = sortedGroupByClause;
    }

    public Map<String, SqlNode> getSelectNodeMap() {
        return selectNodeMap;
    }

    public Map<String, SqlNode> getOrderByNodeMap() {
        return orderByNodeMap;
    }

    public boolean isExclusiveGroupBy() {
        return exclusiveGroupBy;
    }

    public void setExclusiveGroupBy(boolean exclusiveGroupBy) {
        this.exclusiveGroupBy = exclusiveGroupBy;
    }
}
